package cn.yzj.openapi.dao.impl;

import cn.yzj.common.CommonSymbol;
import cn.yzj.openapi.common.ResultCode;
import cn.yzj.openapi.dao.base.TiebaSignBaseDao;
import cn.yzj.openapi.dao.intf.TiebaSignDao;
import cn.yzj.openapi.dto.TiebaSignDTO;
import cn.yzj.openapi.entity.TiebaSignEntity;
import cn.yzj.openapi.enums.error.TiebaSignErrorEnums;
import cn.yzj.openapi.exception.OpenApiException;
import cn.yzj.openapi.utils.NanoIdUtils;
import cn.yzj.openapi.utils.StrUtils;
import cn.yzj.openapi.utils.TiebaUtils;
import cn.yzj.openapi.vo.TiebaVO;
import cn.yzj.utils.GzipUtils;
import com.google.common.cache.Cache;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 贴吧签到底层实现
 *
 * @author gzkemays
 * @since 2022/04/28 20:34
 */
@Repository
@Slf4j
public class TiebaSignDaoImpl extends TiebaSignBaseDao implements TiebaSignDao {
  @Resource(name = "tiebaCache")
  Cache<String, String> cache;

  @Resource(name = "threadPoolExecutor")
  ThreadPoolExecutor executor;

  @Override
  public String sign(String username) {
    log.info("当前 {} 线程 {} ,进行签到.", username, Thread.currentThread().getName());
    String name = checkUsername(username);
    TiebaSignEntity one =
        mapper.findOne(query().selectAll().where.username().eq(name).and.enabled().isTrue().end());
    if (one == null) {
      throw OpenApiException.tiebaSignException(TiebaSignErrorEnums.NOT_FOUND_USER);
    }
    tiebaSign(one);
    return checkSign(username);
  }

  @Override
  public void sign() {
    List<TiebaSignEntity> entities =
        mapper.listEntity(query().selectAll().where.enabled().isTrue().end());
    for (TiebaSignEntity entity : entities) {
      tiebaSign(entity);
      /*      // 每个用户签到后休眠 30 秒
      try {
        Thread.sleep(30 * 1000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }*/
    }
  }

  @Override
  public void test() {
    for (int i = 0; i < 10; i++) {
      executor.execute(
          () -> {
            System.out.println(Thread.currentThread().getName());
          });
    }
  }

  @Override
  public TiebaVO save(TiebaSignDTO dto) {
    String cookie = dto.getCookie();
    boolean relogin = dto.getRelogin() != null && dto.getRelogin();
    TiebaUtils tiebaUtils = TiebaUtils.newInstance(cookie);
    String follows = StrUtils.listToStr(tiebaUtils.getFollows(), CommonSymbol.COMMA);
    String username = tiebaUtils.getUsername();
    TiebaSignEntity one = mapper.findOne(query().where.username().eq(username).end());
    if (!relogin && one != null) {
      throw OpenApiException.tiebaSignException(TiebaSignErrorEnums.USER_RE_LOGIN);
    }
    if (relogin && one != null) {
      one.setCookie(GzipUtils.compress(cookie));
      one.setUsername(username);
      one.setFollowList(follows);
      one.setUpdateTime(new Date());
      updateById(one);
      return new TiebaVO(tiebaUtils);
    }
    TiebaSignEntity entity = new TiebaSignEntity();
    entity.setId(NanoIdUtils.randomNanoId(20));
    entity.setCookie(GzipUtils.compress(cookie));
    entity.setUsername(tiebaUtils.getUsername());
    entity.setFollowList(follows);
    entity.setCreateTime(new Date());
    save(entity);
    return new TiebaVO(tiebaUtils);
  }

  @Override
  public String checkSign(String username) {
    String name = checkUsername(username);
    String msg = cache.getIfPresent(name);
    return Optional.ofNullable(msg)
        .orElseThrow(
            () -> OpenApiException.tiebaSignException(TiebaSignErrorEnums.NOT_SIGN_RECORD));
  }

  private String checkUsername(String username) {
    return Optional.ofNullable(username)
        .orElseThrow(
            () -> OpenApiException.builder().code(ResultCode.DATA_ERROR).msg("用户名不能为空").build());
  }

  private void tiebaSign(TiebaSignEntity entity) {
    try {
      List<String> follows =
          StrUtils.strToList(entity.getFollowList(), CommonSymbol.COMMA, String.class);
      List<TiebaUtils.FollowStatus> sign =
          TiebaUtils.sign(follows, GzipUtils.uncompress(entity.getCookie()));
      // 记录并删除错误的关注
      List<String> errorFollows = new ArrayList<>();
      for (TiebaUtils.FollowStatus followStatus : sign) {
        String follow = followStatus.getFollow();
        String status = followStatus.getStatus();
        if (status.equals(TiebaUtils.REMOVE_CODE)) {
          errorFollows.add(follow);
        }
      }
      // 去掉错误的更新
      if (!CollectionUtils.isEmpty(errorFollows)) {
        follows.removeAll(errorFollows);
        entity.setFollowList(StrUtils.listToStr(follows, CommonSymbol.COMMA));
        updateById(entity);
      }
      cache.put(
          entity.getUsername(),
          sign.isEmpty()
              ? "全部签到完毕"
              : "以下贴吧未签到："
                  + StrUtils.listToStr(errorFollows, CommonSymbol.COMMA)
                  + "\n可能是遭遇贴吧封禁亦或者您取消了关注，因此已经自动删除，如果需要恢复请重新登录 cookie");
    } catch (RuntimeException e) {
      // 签到异常的用户进行关闭
      entity.setEnabled(false);
      if (e instanceof OpenApiException) {
        entity.setReason(((OpenApiException) e).getMsg());
      } else {
        entity.setReason(e.getMessage());
      }
      entity.setUpdateTime(new Date());
      updateById(entity);
      e.printStackTrace();
    }
  }
}
